package kpi.java_training.utils;

import kpi.java_training.domain.questions.*;
import kpi.java_training.domain.questions.Topic;
import kpi.java_training.users.UsersList;

import static kpi.java_training.utils.XmlConstants.*;

import javax.xml.stream.XMLInputFactory;
import static javax.xml.stream.XMLStreamConstants.*;
import javax.xml.stream.XMLStreamReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Alex Kebkalo (kebka@mail.ru) on 25.11.13.
 * Loads data from xml files. Provides methods to load questions and users
 */
public class StaxDataLoader {
    private String fileName;
    private XMLStreamReader reader;

    public StaxDataLoader(String fileName) {
        this.fileName = fileName;
    }

    // TODO: Refactor
    public List<Topic> loadQuestions() throws Exception {
        List<Topic> topics = new ArrayList<>();
        XMLInputFactory factory = XMLInputFactory.newInstance();
        FileReader fileReader = new FileReader(fileName);
        reader = factory.createXMLStreamReader(fileReader);
        while (reader.hasNext()) {
            setNextElement(TOPIC);
            if (!reader.hasNext()) {
                break;
            }
            topics.add(loadTopic());
        }
        reader.close();
        fileReader.close();
        return topics;
    }

    private Topic loadTopic() throws Exception {
        Topic topic = new Topic(getTagText(TOPIC_NAME));
        int event = reader.next();
        while ((event != END_ELEMENT) || !reader.getLocalName().equals(TOPIC)){
            if ((event == START_ELEMENT) && (reader.getLocalName().equals(QUESTION))) {
                Question question = readQuestion();
                topic.add(question);
            }
            event = reader.next();
        }
        return topic;
    }

    private Question readQuestion() throws Exception {
        String type = getTagText(QUESTION_TYPE);
        String questionText = getTagText(QUESTION_TEXT);
        List<String> answerVariants;
        if (type.equals(QUESTION_TYPE_MULTI) || type.equals(QUESTION_TYPE_ONE)) {
            answerVariants = getTagList(ANSWER_VARIANTS, ANSWER_VARIANT);
        } else {
            answerVariants = new ArrayList<>();
        }
        String correctAnswer = getTagText(CORRECT_ANSWER);
        int complexity = Integer.parseInt(getTagText(COMPLEXITY));
        Question question = null;
        switch (type) {
            case QUESTION_TYPE_MULTI:
                question = new MultiAnswersQuestion(questionText, answerVariants, correctAnswer, complexity);
                break;
            case QUESTION_TYPE_ONE:
                question = new OneFromManyAnswersQuestion(questionText, answerVariants, correctAnswer, complexity);
                break;
            case QUESTION_TYPE_TEXT:
                question = new TextAnswerQuestion(questionText, answerVariants, correctAnswer, complexity);
                break;
            case QUESTION_TYPE_YES_NO:
                answerVariants.add("Yes");
                answerVariants.add("No");
                question = new YesNoQuestion(questionText, answerVariants, correctAnswer, complexity);
                break;
        }
        return question;
    }

    private List<String> getTagList(String rootTag, String itemTag) throws Exception{
        List<String> tagList = new ArrayList<>();
        int event = -1;
        while (reader.hasNext() && ((event != END_ELEMENT) || !reader.getLocalName().equals(rootTag))) {
            if ((event == START_ELEMENT) && (reader.getLocalName().equals(itemTag))) {
                while (reader.next() != CHARACTERS) {
                }
                tagList.add(reader.getText());
                while (reader.next() != END_ELEMENT) {
                }
            }
            event = reader.next();
        }
        return tagList;
    }

    private void setNextElement() throws Exception {
        while (reader.hasNext() && (reader.next() != START_ELEMENT)) {
        }
    }

    private void setNextElement(String tagName) throws Exception {
        do {
            setNextElement();
        } while (reader.hasNext() && (!reader.getLocalName().equals(tagName)));
    }

    private String getTagText(String tagName) throws Exception {
        setNextElement(tagName);
        while (reader.next() != CHARACTERS) {
        }
        String text = reader.getText();
        while (reader.next() != END_ELEMENT) {
        }
        return text;
    }

    public List<String> loadAnswers() throws Exception {
        List<String> answers = new ArrayList<>();
        XMLInputFactory factory = XMLInputFactory.newInstance();
        reader = factory.createXMLStreamReader(new FileReader(fileName));
        setNextElement(ANSWERS_SECTION);
        int event = -1;
        while (reader.hasNext() && ((event != END_ELEMENT) || !reader.getLocalName().equals(ANSWERS_SECTION))) {
            if ((event == START_ELEMENT) && (reader.getLocalName().equals(ANSWER))) {
                while (reader.next() != CHARACTERS) {
                }
                answers.add(reader.getText());
                while (reader.next() != END_ELEMENT) {
                }
            }
            event = reader.next();
        }
        reader.close();
        return answers;
    }

    public UsersList loadUsers() throws Exception {
        UsersList users = new UsersList();
        if (!new File(fileName).exists()) {
            return users;
        }
        XMLInputFactory factory = XMLInputFactory.newInstance();
        FileReader fileReader = new FileReader(fileName);
        reader = factory.createXMLStreamReader(fileReader);
        int event = -1;
        while (reader.hasNext() && ((event != END_ELEMENT) || !reader.getLocalName().equals(USERS_SECTION))) {
            if ((event == START_ELEMENT) && (reader.getLocalName().equals(USER))) {
                String userName = getTagText(USER_NAME);
                String password = getTagText(USER_PASSWORD);
                users.addUser(userName, password);
            }
            event = reader.next();
        }
        reader.close();
        fileReader.close();
        return users;
    }
}
